package geom;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.Rectangle2D;

import doc.SampledPoint;

public class Rect extends Rectangle2D.Double {

	private double angle;

	public Rect() {
		this(0, 0, 0, 0, 0);
	}

	public Rect(Point ref, double width, double height) {
		this(ref.getX(), ref.getY(), width, height, 0);
	}

	public Rect(double x, double y, double w, double h) {
		this(x, y, w, h, 0);
	}

	public Rect(double x, double y, double w, double h, double a) {
		super(x, y, w, h);
		angle = a;
	}

	public double getAngle() {
		return angle;
	}

	public void setAngle(double angle) {
		this.angle = angle;
	}

	public void setX(double x) {
		setRect(x, getY(), getWidth(), getHeight());
	}

	public void setY(double y) {
		setRect(getX(), getY(), getWidth(), getHeight());
	}

	public void setWidth(double w) {
		setRect(getX(), getY(), w, getHeight());
	}

	public void setHeight(double h) {
		setRect(getX(), getY(), getWidth(), h);
	}

	public Point ulPoint() {
		return new Point(getX(), getY());
	}

	public Point getRefPoint() {
		return ulPoint();
	}

	public Point urPoint() {
		return getRefPoint().add(rightVector());
	}

	public Point lrPoint() {
		return getRefPoint().add(rightVector().add(downVector()));
	}

	public Point llPoint() {
		return getRefPoint().add(downVector());
	}

	public gVector rightVector() {
		return new gVector(getWidth() * Math.cos(getAngle()),
						   -getWidth() * Math.sin(getAngle()));
	}

	public gVector downVector() {
		return new gVector(getHeight() * Math.sin(getAngle()),
						   getHeight() * Math.cos(getAngle()));
	}

	public Polygon toPolygon() {
		int n = 4,
			x[] = new int[n],
			y[] = new int[n];
		x[0] = ulPoint().intX(); y[0] = ulPoint().intY();
		x[1] = urPoint().intX(); y[1] = urPoint().intY();
		x[3] = llPoint().intX(); y[3] = llPoint().intY();
		x[2] = lrPoint().intX(); y[2] = lrPoint().intY();
		return new Polygon(x, y, n);
	}

	public void draw(Graphics2D g2) {
		draw(g2, Color.BLUE, 1);
	}

	public void draw(Graphics2D g2, Color color, int width) {
		g2.setColor(color);
		g2.setStroke(new BasicStroke(width));


		g2.draw(toPolygon());

//		drawParam(g2);
	}

	public void drawParam(Graphics2D g2) {
		drawParam(g2, Color.RED, 1);
	}

	public void drawParam(Graphics2D g2, Color c, int w) {
		g2.setColor(c);
		g2.setStroke(new BasicStroke(w));
		int LEN = 30;
		Point p = getRefPoint();
		g2.drawLine(p.intX(), p.intY(), p.intX() + LEN, p.intY());
	}

	public static Rect ofBoundBetween(SampledPoint[] ps, int s, int e) {
		SampledPoint[] pp = new SampledPoint[e - s];
		for (int i = 0; i < pp.length; i++) {
			pp[i] = ps[i+s];
		}
		return ofBound(pp);
	}



	public static Rect ofBound(SampledPoint[] ps) {
		int X = 0, Y = 0;
		double[][] disMatrix = new double[ps.length][];
		for (int i = 0; i < disMatrix.length; i++) {
			disMatrix[i] = new double[ps.length];

			for (int j = i; j < ps.length; j++) {
				disMatrix[i][j] = ps[i].toPoint().distance(ps[j].toPoint());
				if (disMatrix[i][j] > disMatrix[X][Y]) {
					X = i;
					Y = j;
				}
			}
		}

		Line line = new Line(ps[X].toPoint(), ps[Y].toPoint());
		double lDis = 0;
		double uDis = 0;
		for (int i = 0; i < ps.length; i++) {
			double d = Math.abs(line.ptLineDist(ps[i].toPoint()));
			gVector v = ps[i].toPoint().subtract(ps[X].toPoint()).dir();

			double angle = line.dir().angleWith(v);
			if (angle > Math.PI) {
				uDis = Math.max(uDis, d);
			} else {
				lDis = Math.max(lDis, d);
			}
		}

		double w = ps[X].toPoint().distance(ps[Y].toPoint());
		double h = uDis + lDis;
		double u;
		double cos = (ps[Y].getX() - ps[X].getX()) / w,
			   sin = (ps[Y].getY() - ps[X].getY()) / w;
		Point start;
		if (cos > 0) {
			start = ps[X].toPoint();
			u = uDis;
		} else {
			start = ps[Y].toPoint();
			u = lDis;
			sin = -sin;
		}

		Point ref = start.add(u * sin, - u * Math.abs(cos));

		double a = Math.acos(Math.abs(cos));
		if (sin > 0)
			a = 2 * Math.PI - a;

		return new Rect(ref.getX(), ref.getY(), w, h, a);
	}

	public Line getMajorAxis() {
		Point p1 = ulPoint().linearInterpolate(llPoint(), 0.5);
		Point p2 = urPoint().linearInterpolate(lrPoint(), 0.5);
		return new Line(p1, p2);

	}

	public Line getMinorAxis() {
		Point p1 = ulPoint().linearInterpolate(urPoint(), 0.5);
		Point p2 = llPoint().linearInterpolate(lrPoint(), 0.5);
		return new Line(p1, p2);

	}
}
